package xworker.netty.handlers.http;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.CharsetUtil;
import org.xmeta.util.ExceptionUtil;
import xworker.lang.util.JacksonFormator;
import xworker.lang.util.UtilTemplate;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class HttpHandlerManager {
    static final List<HttpHandlerInfo>  httpHandlers = new ArrayList<>();
    static final List<HttpHandlerInfo> filters = new ArrayList<>();

    public static void regist(Object object){
        regist(object.getClass(), object);
    }

    public static void regist(Class<?> cls, Object object){
        for(Method method : cls.getMethods()) {
            RequestMapping annotation = method.getAnnotation(RequestMapping.class);
            //System.out.println(method.getName() + ":" + annotation);
            if(annotation != null) {
                if(annotation.filter()){
                    filters.add(new HttpHandlerInfo(annotation, method, object));
                }else {
                    httpHandlers.add(new HttpHandlerInfo(annotation, method, object));
                }
            }
        }
    }

    private static FullHttpResponse invokeHnadler(RequestBean requestBean, HttpHandlerInfo info, boolean filter) throws Throwable {
        Object result = info.handleRquest();
        if(result instanceof FullHttpResponse){
            return (FullHttpResponse) result;
        }

        HttpResponseStatus status = HttpHandler.getHttpResponseStatus();
        if(status == null){
            status = HttpResponseStatus.OK;
        }else if(status == HttpResponseStatus.FOUND){
            //redirect
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FOUND, Unpooled.EMPTY_BUFFER);
            response.headers().set(HttpHeaderNames.LOCATION, String.valueOf(result));

            return response;
        }

        String contenType = HttpHandler.getResponseContentType();
        ByteBuf out;
        String resultHandler = HttpHandler.getResultHandler();
        if(HttpHandler.JSON.equals(resultHandler)){
            String json = JacksonFormator.formatObject(result);
            out = Unpooled.copiedBuffer(json, CharsetUtil.UTF_8);
            if(contenType == null || contenType.isEmpty()) {
                contenType = "text/plain; charset=UTF-8";
            }
        }else if(HttpHandler.FREEMARKER.equals(resultHandler)){
            String html = UtilTemplate.process(requestBean.getActionContext(), (String) result, "freemarker");
            if(contenType == null || contenType.isEmpty()) {
                contenType = "text/html; charset=UTF-8";
            }
            assert html != null;
            out = Unpooled.copiedBuffer(html, CharsetUtil.UTF_8);
        }else if(HttpHandler.REDIRECT.equals(resultHandler)){
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FOUND, Unpooled.EMPTY_BUFFER);
            response.headers().set(HttpHeaderNames.LOCATION, String.valueOf(result));

            return response;
        }else if(result instanceof String){
            out = Unpooled.copiedBuffer((String) result, CharsetUtil.UTF_8);
        }else if(result instanceof ByteBuf){
            out = (ByteBuf) result;
        }else if(result instanceof byte[]){
            out = Unpooled.wrappedBuffer((byte[]) result);
        }else if(result != null){
            out = Unpooled.copiedBuffer(String.valueOf(result), CharsetUtil.UTF_8);
        }else{
            if(filter){
                return null;
            }

            out = Unpooled.EMPTY_BUFFER;
        }

        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, out);
        if(!(contenType == null || contenType.isEmpty())){
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, contenType);
        }

        return response;
    }

    public static FullHttpResponse handleRequest(RequestBean requestBean, ChannelHandlerContext ctx, String uri){
        try{
            HttpHandler.setRequestBean(requestBean);
            HttpHandler.setChannelHandlerContext(ctx);

            int index = uri.indexOf("?");
            if(index != -1){
                uri = uri.substring(0, index);
            }

            //处理过滤器
            for(HttpHandlerInfo filter : filters){
                if(filter.accept(uri)){
                    FullHttpResponse response = invokeHnadler(requestBean, filter, true);
                    if(response != null){
                        HttpHandler.setHandled(true);
                        return response;
                    }
                }
            }

            //处理Handler
            for(HttpHandlerInfo info : httpHandlers){
                if(info.accept(uri)){
                    HttpHandler.setHandled(true);
                    return invokeHnadler(requestBean, info, false);
                }
            }

            return null;
        }catch(Throwable e){
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR,
                    Unpooled.copiedBuffer(ExceptionUtil.toString(e), CharsetUtil.UTF_8));

            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
            return response;
        }finally {
            HttpHandler.clear();
        }
    }
}